package org.china.lanzhou.grid;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.china.lanzhou.Globe;
import org.china.lanzhou.atom.AtomManager;
import org.china.lanzhou.atom.LinkAtom;
import org.china.lanzhou.common.utils.Threads;
import org.china.lanzhou.exception.NotFoundElementException;
import org.china.lanzhou.manager.AsyncManager;

import java.util.*;

/**
 * Created by Administrator on 2020\1\9 0009.
 */
@Slf4j
public final class SphereGrid implements Grid{
    // 原子索引库
    private final transient Hashtable<Integer,Element> integerElementHashtable = new Hashtable<>();

    private SequenceGeneration sequenceGeneration = null;

    private boolean clutch  = false; // 离  /  合

    @Override
    public void install(AtomManager atomManager) {
        atomManager.verify();
        for (LinkAtom link : atomManager.getLinks()) {
            Element targetE = null;
            if(Objects.nonNull(link.getTarget()) && Objects.isNull(indexes(link.getTarget().getValue()))){
                targetE = createElement(link.getTarget().getValue());
            }
            if(Objects.nonNull(link.getAtom()) && Objects.isNull(indexes(link.getAtom().getValue()))){
                createElement(link.getAtom().getValue());
            }
            if(Objects.nonNull(link.getAtom()) && Objects.nonNull(link.getTarget()))
            sticky(link.getAtom().getValue(),link.getTarget().getValue(),link.getReaction());
        }
    }


    @Override
    public void uninstall(AtomManager atomManager) {
        atomManager.verify();
        for (LinkAtom link : atomManager.getLinks()) {
            disSticky(link.getAtom().getValue(),link.getTarget().getValue());
            integerElementHashtable.remove(link.getAtom().getValue());
            integerElementHashtable.remove(link.getTarget().getValue());
        }
    }

    /**
     * 触发元素
     *
     * @param index
     */
    @Override
    public void trigger(int index) {

    }


    /**
     * 离合器
     * @param time
     */
    @Override
    public void clutch(int time) {
        if(time<=0){
            this.clutch=true;
        }
        if(time==Integer.MAX_VALUE){
            this.clutch=false;
        }
        AsyncManager.me().execute(new TimerTask() {
            @SneakyThrows(InterruptedException.class)
            @Override
            public void run() {
                clutch(0);
                Thread.sleep(time);
                clutch(Integer.MAX_VALUE);
            }
        });
    }

    /**
     * 链式返回匹配值
     * @param index 主题
     * @param antenna 动作
     * @return
     */
    @Override
    public Set<Element> selectElements(int index, int antenna) {
        boolean contains = ElementUtils.mapContainsKey(get(index).getAntennaMap(),antenna);
         if(contains) {
             Set<Element> elementSet = get(index).getAntennaMap().keySet();
             elementSet.retainAll(get(antenna).getAntennaMap().keySet());
             return elementSet;
         }
        return null;
    }

    /**
     * 链式返回匹配值
     * @param index 主题
     * @param antennas 连续动作
     * @return
     */
    @Override
    public Set<Element> selectElements(int index, int... antennas) {
        Map<Element, Double> antennaMap = get(index).getAntennaMap();
        for (int antenna : antennas) {
            boolean contains = ElementUtils.mapContainsKey(antennaMap,antenna);
            if(contains) {
                antennaMap = ElementUtils.getContainsKey(antennaMap,antenna).getAntennaMap();
            }else{
                return null;
            }
        }
        Set<Element> elementSet = get(index).getAntennaMap().keySet();
        elementSet.retainAll(antennaMap.keySet());
        return elementSet;
    }

    /**
     * 元素之间
     */
    @Override
    public double Related(int index, int target) {

        Set<Element> elementSet = get(index).getAntennaMap().keySet();
        if(elementSet.contains(get(target))){
            return get(index).getAntennaMap().get(get(target));
        }
        return 0;
    }

    /**
     * 返回 index
     * @param index
     * @return
     */
    @Override
    public Element get(int index){
        return indexes(index);
    }

    /**
     * 判断是否存在
     * @param index
     * @return
     */
    @Override
    public boolean contains(int index){
        return integerElementHashtable.contains(index);
    }

    /**
     *  主动遗忘,移除该元素，
     *  在适当（不使用）的时候选择遗忘
     * @param index
     */
    @Override
    public void forget(int index){
        disable(index,true);
        // 等待后异步操作
        AsyncManager.me().execute(new TimerTask() {
            @Override
            public void run() {
                Element element = indexes(index);
                for (int i = 0; i < 30; i++)
                if (element.getQueue().peek().toString().startsWith(Globe.QUEUE_LOG_END)) { // 判断标记为已退出
                    Threads.sleep(300);
                    List<Element> affinity = element.getAffinity();
                    for (Element element1 : affinity) {
                        element1.getAntennaMap().remove(element);
                    }
                    // 删除元素
                    integerElementHashtable.remove(element);
                    element = null;
                    return;
                }
            }
        });
    }

    /**
     * 元素失效,
     * 元素连接到失效属性,配合触发器操作
     * @param index
     * @param disable
     */
    @Override
    public void disable(int index, boolean disable){
        if(disable)
        sticky(index,Globe.DISABLE_ID,1);
        else
        disSticky(index,Globe.DISABLE_ID);
    }

    /**
     * 创建一个元素并添加索引
     * @param index
     * @return
     */
    @Override
    public Element createElement(int index){
        Element element = indexes(index);
        if(Objects.isNull(element)){
            element = new Element();
        }else{
            return element;
        }
        element.setIndex(index);
        addIndexes(element);
        return element;
    }

    // 查询索引
    private Element indexes(Integer index){
    try {
        return findIndexes(index);
    } catch (NotFoundElementException e) {
        log.info("Exception has been catched: {}", e.getMessage());
    }
    return null;
}
    // 查询索引
    private Element findIndexes(Integer index){
        Element element = integerElementHashtable.get(index);
        if(Objects.isNull(element))
            throw new NotFoundElementException();
        return element;
    }
    // 添加索引
    private void addIndexes(Element element){
        integerElementHashtable.put(element.getIndex(),element);
    }

    /**
     * 元素粘粘 for element
     * @param element
     * @param targetElement
     * @param degree
     */
    public void sticky(Element element,Element targetElement,double degree){
        Map<Element,Double> antennaMap = element.getAntennaMap();
                if(Objects.isNull(antennaMap.get(targetElement))){
                    if(!antennaMap.containsKey(targetElement))
                    {antennaMap.put(targetElement,degree);}
                    else{
                        // 增强
                    }
                    if(!targetElement.getAffinity().contains(element))
                    {targetElement.getAffinity().add(element);}
                    else{
                        // 增强
                    }
                    log.info("sticky complete {} to {}",element.getIndex(),targetElement.getIndex());
                }else{
                    log.info("sticky existence {} to {}",element.getIndex(),targetElement.getIndex());
                }
    }

    /**
     * 元素粘粘 for index
     * @param index
     * @param targetIndex
     * @param degree
     */
    @Override
    public void sticky(int index, int targetIndex, double degree){
        Element element = findIndexes(index);
        Element targetElement = findIndexes(targetIndex);
        sticky(element,targetElement,degree);
    }
    // 粘粘解除
    @Override
    public void disSticky(int index,int targetIndex){
        Element element = findIndexes(index);
        Element targetElement = findIndexes(targetIndex);
        element.getAntennaMap().remove(targetElement);
        targetElement.getAffinity().add(element);
    }
    @Override
    public void print(){
        for (Map.Entry<Integer, Element> integerElementEntry : this.integerElementHashtable.entrySet()) {
            System.out.println(integerElementEntry.getKey()+" : "+integerElementEntry.getValue().getIndex());
        }
    }

}
